Tests for various python features.     vim: set ft=vim :

NOTE: This will cause errors when run under valgrind.
This would require recompiling Python with:
  ./configure --without-pymalloc
See http://svn.python.org/view/python/trunk/Misc/README.valgrind?view=markup

STARTTEST
:so small.vim
:set encoding=latin1
:set noswapfile
:if !has('python') || !has('quickfix') | e! test.ok | wq! test.out | endif
:lang C
:fun Test()
:py import vim
:py cb = vim.current.buffer
:let l = []
:py l=vim.bindeval('l')
:py f=vim.bindeval('function("strlen")')
:" Extending List directly with different types
:py l.extend([1, "as'd", [1, 2, f, {'a': 1}]])
:$put =string(l)
:$put =string(l[-1])
:try
:  $put =string(l[-4])
:catch
:  $put =v:exception[:13]
:endtry
:" List assignment
:py l[0]=0
:$put =string(l)
:py l[-2]=f
:$put =string(l)
:"
:" Extending Dictionary directly with different types
:let d = {}
:fun d.f()
:  return 1
:endfun
py << trim EOF
  d=vim.bindeval('d')
  d['1']='asd'
  d.update()  # Must not do anything, including throwing errors
  d.update(b=[1, 2, f])
  d.update((('-1', {'a': 1}),))
  d.update({'0': -1})
  dk = d.keys()
  dv = d.values()
  di = d.items()
  cmpfun = lambda a, b: cmp(repr(a), repr(b))
  dk.sort(cmpfun)
  dv.sort(cmpfun)
  di.sort(cmpfun)
EOF
:$put =pyeval('d[''f''](self={})')
:$put =pyeval('repr(dk)')
:$put =substitute(pyeval('repr(dv)'),'0x\x\+','','g')
:$put =substitute(pyeval('repr(di)'),'0x\x\+','','g')
:for [key, Val] in sort(items(d))
:  $put =string(key) . ' : ' . string(Val)
:  unlet key Val
:endfor
:py del dk
:py del di
:py del dv
:"
:" removing items with del
:py del l[2]
:$put =string(l)
:let l = range(8)
:py l=vim.bindeval('l')
:try
:   py del l[:3]
:   py del l[1:]
:catch
:   $put =v:exception
:endtry
:$put =string(l)
:"
:py del d['-1']
:py del d['f']
:$put =string(pyeval('d.get(''b'', 1)'))
:$put =string(pyeval('d.pop(''b'')'))
:$put =string(pyeval('d.get(''b'', 1)'))
:$put =string(pyeval('d.pop(''1'', 2)'))
:$put =string(pyeval('d.pop(''1'', 2)'))
:$put =pyeval('repr(d.has_key(''0''))')
:$put =pyeval('repr(d.has_key(''1''))')
:$put =pyeval('repr(''0'' in d)')
:$put =pyeval('repr(''1'' in d)')
:$put =pyeval('repr(list(iter(d)))')
:$put =string(d)
:$put =pyeval('repr(d.popitem())')
:$put =pyeval('repr(d.get(''0''))')
:$put =pyeval('repr(list(iter(d)))')
:"
:" removing items out of range: silently skip items that don't exist
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:" The following two ranges delete nothing as they match empty list:
:py del l[2:1]
:$put =string(l)
:py del l[2:2]
:$put =string(l)
:py del l[2:3]
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py del l[2:4]
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py del l[2:5]
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py del l[2:6]
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:" The following two ranges delete nothing as they match empty list:
:py del l[-1:2]
:$put =string(l)
:py del l[-2:2]
:$put =string(l)
:py del l[-3:2]
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py del l[-4:2]
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py del l[-5:2]
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py del l[-6:2]
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py del l[::2]
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py del l[3:0:-2]
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py del l[2:4:-2]
:$put =string(l)
:"
:" Slice assignment to a list
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py l[0:0]=['a']
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py l[1:2]=['b']
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py l[2:4]=['c']
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py l[4:4]=['d']
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py l[-1:2]=['e']
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py l[-10:2]=['f']
:$put =string(l)
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:py l[2:-10]=['g']
:$put =string(l)
:let l = []
:py l=vim.bindeval('l')
:py l[0:0]=['h']
:$put =string(l)
:let l = range(8)
:py l=vim.bindeval('l')
:py l[2:6:2] = [10, 20]
:$put =string(l)
:let l = range(8)
:py l=vim.bindeval('l')
:py l[6:2:-2] = [10, 20]
:$put =string(l)
:let l = range(8)
:py l=vim.bindeval('l')
:py l[6:2] = ()
:$put =string(l)
:let l = range(8)
:py l=vim.bindeval('l')
:py l[6:2:1] = ()
:$put =string(l)
:let l = range(8)
:py l=vim.bindeval('l')
:py l[2:2:1] = ()
:$put =string(l)
:"
:" Locked variables
:let l = [0, 1, 2, 3]
:py l=vim.bindeval('l')
:lockvar! l
py << trim EOF
  def emsg(ei):
      return ei[0].__name__ + ':' + repr(ei[1].args)

  try:
      l[2]='i'
  except vim.error:
      cb.append('l[2] threw vim.error: ' + emsg(sys.exc_info()))
EOF
:$put =string(l)
:unlockvar! l
:"
:" Function calls
py << trim EOF
  import sys
  def ee(expr, g=globals(), l=locals()):
      try:
          exec(expr, g, l)
      except:
          ei = sys.exc_info()
          msg = emsg(ei)
          msg = msg.replace('TypeError:(\'argument 1 ', 'TypeError:(\'')
          if expr.find('None') > -1:
              msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
                                'TypeError:("\'NoneType\' object is not iterable",)')
          if expr.find('FailingNumber') > -1:
              msg = msg.replace(', not \'FailingNumber\'', '').replace('"', '\'')
              msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
                                'TypeError:("\'FailingNumber\' object is not iterable",)')
          if msg.find('(\'\'') > -1 or msg.find('(\'can\'t') > -1:
              msg = msg.replace('(\'', '("').replace('\',)', '",)')
          # Some Python versions say can't, others cannot.
          if msg.find('can\'t') > -1:
              msg = msg.replace('can\'t', 'cannot')
          # Some Python versions use single quote, some double quote
          if msg.find('"cannot ') > -1:
              msg = msg.replace('"cannot ', '\'cannot ')
          if msg.find(' attributes"') > -1:
              msg = msg.replace(' attributes"', ' attributes\'')
          if expr == 'fd(self=[])':
              # HACK: PyMapping_Check changed meaning
              msg = msg.replace('AttributeError:(\'keys\',)',
                                'TypeError:(\'unable to convert list to vim dictionary\',)')
          vim.current.buffer.append(expr + ':' + msg)
      else:
          vim.current.buffer.append(expr + ':NOT FAILED')
EOF
:fun New(...)
:   return ['NewStart']+a:000+['NewEnd']
:endfun
:fun DictNew(...) dict
:   return ['DictNewStart']+a:000+['DictNewEnd', self]
:endfun
:let l=[function('New'), function('DictNew')]
:py l=vim.bindeval('l')
:py l.extend(list(l[0](1, 2, 3)))
:$put =string(l)
:py l.extend(list(l[1](1, 2, 3, self={'a': 'b'})))
:$put =string(l)
:py l.extend([l[0].name])
:$put =string(l)
:py ee('l[1](1, 2, 3)')
:py f=l[0]
:delfunction New
:py ee('f(1, 2, 3)')
:if has('float')
:   let l=[0.0]
:   py l=vim.bindeval('l')
:   py l.extend([0.0])
:   $put =string(l)
:else
:   $put ='[0.0, 0.0]'
:endif
:let messages=[]
:delfunction DictNew
py << trim EOF
  d=vim.bindeval('{}')
  m=vim.bindeval('messages')
  def em(expr, g=globals(), l=locals()):
      try:
          exec(expr, g, l)
      except:
          m.extend([sys.exc_type.__name__])

  em('d["abc1"]')
  em('d["abc1"]="\\0"')
  em('d["abc1"]=vim')
  em('d[""]=1')
  em('d["a\\0b"]=1')
  em('d[u"a\\0b"]=1')

  em('d.pop("abc1")')
  em('d.popitem()')
  del em
  del m
EOF
:$put =messages
:unlet messages
:" locked and scope attributes
:let d={} | let dl={} | lockvar dl
:for s in split("d dl v: g:")
:    let name=tr(s, ':', 's')
:    execute 'py '.name.'=vim.bindeval("'.s.'")'
:    let toput=s.' : '.join(map(['locked', 'scope'], 'v:val.":".pyeval(name.".".v:val)'), ';')
:    $put =toput
:endfor
:silent! let d.abc2=1
:silent! let dl.abc3=1
:py d.locked=True
:py dl.locked=False
:silent! let d.def=1
:silent! let dl.def=1
:put ='d:'.string(d)
:put ='dl:'.string(dl)
:unlet d dl
:
:let l=[] | let ll=[] | lockvar ll
:for s in split("l ll")
:    let name=tr(s, ':', 's')
:    execute 'py '.name.'=vim.bindeval("'.s.'")'
:    let toput=s.' : locked:'.pyeval(name.'.locked')
:    $put =toput
:endfor
:silent! call extend(l, [0])
:silent! call extend(ll, [0])
:py l.locked=True
:py ll.locked=False
:silent! call extend(l, [1])
:silent! call extend(ll, [1])
:put ='l:'.string(l)
:put ='ll:'.string(ll)
:unlet l ll
:"
:" pyeval()
:let l=pyeval('range(3)')
:$put =string(l)
:let d=pyeval('{"a": "b", "c": 1, "d": ["e"]}')
:$put =sort(items(d))
:let v:errmsg = ''
:$put ='pyeval(\"None\") = ' . pyeval('None') . v:errmsg
:if has('float')
:   let f=pyeval('0.0')
:   $put =string(f)
:else
:   $put ='0.0'
:endif
:" Invalid values:
:for e in ['"\0"', '{"\0": 1}', 'undefined_name', 'vim']
:   try
:      let v=pyeval(e)
:   catch
:      let toput=e.":\t".v:exception[:13]
:      $put =toput
:   endtry
:endfor
:"
:" threading
:let l = [0]
:py l=vim.bindeval('l')
py << trim EOF
  import threading
  import time

  class T(threading.Thread):
      def __init__(self):
          threading.Thread.__init__(self)
          self.t = 0
          self.running = True

      def run(self):
          while self.running:
              self.t += 1
              time.sleep(0.1)

  t = T()
  del T
  t.start()
EOF
:sleep 1
:py t.running = False
:py t.join()
:" Check if the background thread is working.  Count should be 10, but on a
:" busy system (AppVeyor) it can be much lower.
:py l[0] = t.t > 4
:py del time
:py del threading
:py del t
:$put =string(l)
:"
:" settrace
:let l = []
:py l=vim.bindeval('l')
py << trim EOF
  import sys

  def traceit(frame, event, arg):
      global l
      if event == "line":
          l.extend([frame.f_lineno])
      return traceit

  def trace_main():
      for i in range(5):
          pass
EOF
:py sys.settrace(traceit)
:py trace_main()
:py sys.settrace(None)
:py del traceit
:py del trace_main
:$put =string(l)
:"
:" Slice
:py ll = vim.bindeval('[0, 1, 2, 3, 4, 5]')
:py l = ll[:4]
:$put =string(pyeval('l'))
:py l = ll[2:]
:$put =string(pyeval('l'))
:py l = ll[:-4]
:$put =string(pyeval('l'))
:py l = ll[-2:]
:$put =string(pyeval('l'))
:py l = ll[2:4]
:$put =string(pyeval('l'))
:py l = ll[4:2]
:$put =string(pyeval('l'))
:py l = ll[-4:-2]
:$put =string(pyeval('l'))
:py l = ll[-2:-4]
:$put =string(pyeval('l'))
:py l = ll[:]
:$put =string(pyeval('l'))
:py l = ll[0:6]
:$put =string(pyeval('l'))
:py l = ll[-10:10]
:$put =string(pyeval('l'))
:py l = ll[4:2:-1]
:$put =string(pyeval('l'))
:py l = ll[::2]
:$put =string(pyeval('l'))
:py l = ll[4:2:1]
:$put =string(pyeval('l'))
:py del l
:"
:" Vars
:let g:foo = 'bac'
:let w:abc3 = 'def'
:let b:baz = 'bar'
:let t:bar = 'jkl'
:try
:  throw "Abc"
:catch
:  put =pyeval('vim.vvars[''exception'']')
:endtry
:put =pyeval('vim.vars[''foo'']')
:put =pyeval('vim.current.window.vars[''abc3'']')
:put =pyeval('vim.current.buffer.vars[''baz'']')
:put =pyeval('vim.current.tabpage.vars[''bar'']')
:"
:" Options
:" paste:          boolean, global
:" previewheight   number,  global
:" operatorfunc:   string,  global
:" number:         boolean, window-local
:" numberwidth:    number,  window-local
:" colorcolumn:    string,  window-local
:" statusline:     string,  window-local/global
:" autoindent:     boolean, buffer-local
:" shiftwidth:     number,  buffer-local
:" omnifunc:       string,  buffer-local
:" preserveindent: boolean, buffer-local/global
:" path:           string,  buffer-local/global
:let g:bufs=[bufnr('%')]
:new
:let g:bufs+=[bufnr('%')]
:vnew
:let g:bufs+=[bufnr('%')]
:wincmd j
:vnew
:let g:bufs+=[bufnr('%')]
:wincmd l
:fun RecVars(opt)
:  let gval =string(eval('&g:'.a:opt))
:  let wvals=join(map(range(1, 4),  'v:val.":".string(getwinvar(v:val, "&".a:opt))'))
:  let bvals=join(map(copy(g:bufs), 'v:val.":".string(getbufvar(v:val, "&".a:opt))'))
:  put ='  G: '.gval
:  put ='  W: '.wvals
:  put ='  B: '.wvals
:endfun
py << trim EOF
  def e(s, g=globals(), l=locals()):
      try:
          exec(s, g, l)
      except:
          vim.command('return ' + repr(sys.exc_type.__name__))

  def ev(s, g=globals(), l=locals()):
      try:
          return eval(s, g, l)
      except:
          vim.command('let exc=' + repr(sys.exc_type.__name__))
          return 0
EOF
:fun E(s)
:   python e(vim.eval('a:s'))
:endfun
:fun Ev(s)
:   let r=pyeval('ev(vim.eval("a:s"))')
:   if exists('exc')
:       throw exc
:   endif
:   return r
:endfun
:py gopts1=vim.options
:py wopts1=vim.windows[2].options
:py wopts2=vim.windows[0].options
:py wopts3=vim.windows[1].options
:py bopts1=vim.buffers[vim.bindeval("g:bufs")[2]].options
:py bopts2=vim.buffers[vim.bindeval("g:bufs")[1]].options
:py bopts3=vim.buffers[vim.bindeval("g:bufs")[0]].options
:$put ='wopts iters equal: '.pyeval('list(wopts1) == list(wopts2)')
:$put ='bopts iters equal: '.pyeval('list(bopts1) == list(bopts2)')
:py gset=set(iter(gopts1))
:py wset=set(iter(wopts1))
:py bset=set(iter(bopts1))
:set path=.,..,,
:let lst=[]
:let lst+=[['paste',          1,     0,     1,     2,      1,    1,      0    ]]
:let lst+=[['previewheight',  5,     1,     6,     'a',    0,    1,      0    ]]
:let lst+=[['operatorfunc',   'A',   'B',   'C',   2,      0,    1,      0    ]]
:let lst+=[['number',         0,     1,     1,     0,      1,    0,      1    ]]
:let lst+=[['numberwidth',    2,     3,     5,     -100,   0,    0,      1    ]]
:let lst+=[['colorcolumn',    '+1',  '+2',  '+3',  'abc4',  0,    0,      1    ]]
:let lst+=[['statusline',     '1',   '2',   '4',   0,      0,    1,      1    ]]
:let lst+=[['autoindent',     0,     1,     1,     2,      1,    0,      2    ]]
:let lst+=[['shiftwidth',     0,     2,     1,     3,      0,    0,      2    ]]
:let lst+=[['omnifunc',       'A',   'B',   'C',   1,      0,    0,      2    ]]
:let lst+=[['preserveindent', 0,     1,     1,     2,      1,    1,      2    ]]
:let lst+=[['path',           '.,,', ',,',  '.',   0,      0,    1,      2    ]]
:for       [oname,            oval1, oval2, oval3, invval, bool, global, local] in lst
:   py oname=vim.eval('oname')
:   py oval1=vim.bindeval('oval1')
:   py oval2=vim.bindeval('oval2')
:   py oval3=vim.bindeval('oval3')
:   if invval is 0 || invval is 1
:       py invval=bool(vim.bindeval('invval'))
:   else
:       py invval=vim.bindeval('invval')
:   endif
:   if bool
:       py oval1=bool(oval1)
:       py oval2=bool(oval2)
:       py oval3=bool(oval3)
:   endif
:   put ='>>> '.oname
:   $put ='  g/w/b:'.pyeval('oname in gset').'/'.pyeval('oname in wset').'/'.pyeval('oname in bset')
:   $put ='  g/w/b (in):'.pyeval('oname in gopts1').'/'.pyeval('oname in wopts1').'/'.pyeval('oname in bopts1')
:   for v in ['gopts1', 'wopts1', 'bopts1']
:       try
:           put ='  p/'.v.': '.Ev('repr('.v.'['''.oname.'''])')
:       catch
:           put ='  p/'.v.'! '.v:exception
:       endtry
:       let r=E(v.'['''.oname.''']=invval')
:       if r isnot 0
:           put ='  inv: '.string(invval).'! '.r
:       endif
:       for vv in (v is# 'gopts1' ? [v] : [v, v[:-2].'2', v[:-2].'3'])
:           let val=substitute(vv, '^.opts', 'oval', '')
:           let r=E(vv.'['''.oname.''']='.val)
:           if r isnot 0
:               put ='  '.vv.'! '.r
:           endif
:       endfor
:   endfor
:   call RecVars(oname)
:   for v in ['wopts3', 'bopts3']
:       let r=E('del '.v.'["'.oname.'"]')
:       if r isnot 0
:           put ='  del '.v.'! '.r
:       endif
:   endfor
:   call RecVars(oname)
:endfor
:delfunction RecVars
:delfunction E
:delfunction Ev
:py del ev
:py del e
:only
:for buf in g:bufs[1:]
:   execute 'bwipeout!' buf
:endfor
:py del gopts1
:py del wopts1
:py del wopts2
:py del wopts3
:py del bopts1
:py del bopts2
:py del bopts3
:py del oval1
:py del oval2
:py del oval3
:py del oname
:py del invval
:"
:" Test buffer object
:vnew
:put ='First line'
:put ='Second line'
:put ='Third line'
:1 delete _
:py b=vim.current.buffer
:wincmd w
:mark a
:augroup BUFS
:   autocmd BufFilePost * python cb.append(vim.eval('expand("<abuf>")') + ':BufFilePost:' + vim.eval('bufnr("%")'))
:   autocmd BufFilePre * python cb.append(vim.eval('expand("<abuf>")') + ':BufFilePre:' + vim.eval('bufnr("%")'))
:augroup END
py << trim EOF
  # Tests BufferAppend and BufferItem
  cb.append(b[0])
  # Tests BufferSlice and BufferAssSlice
  cb.append('abc5') # Will be overwritten
  cb[-1:] = b[:-2]
  # Test BufferLength and BufferAssSlice
  cb.append('def') # Will not be overwritten
  cb[len(cb):] = b[:]
  # Test BufferAssItem and BufferMark
  cb.append('ghi') # Will be overwritten
  cb[-1] = repr((len(cb) - cb.mark('a')[0], cb.mark('a')[1]))
  # Test BufferRepr
  cb.append(repr(cb) + repr(b))
  # Modify foreign buffer
  b.append('foo')
  b[0]='bar'
  b[0:0]=['baz']
  vim.command('call append("$", getbufline(%i, 1, "$"))' % b.number)
  # Test assigning to name property
  import os
  old_name = cb.name
  cb.name = 'foo'
  cb.append(cb.name[-11:].replace(os.path.sep, '/'))
  b.name = 'bar'
  cb.append(b.name[-11:].replace(os.path.sep, '/'))
  cb.name = old_name
  cb.append(cb.name[-17:].replace(os.path.sep, '/'))
  del old_name
  # Test CheckBuffer
  for _b in vim.buffers:
      if _b is not cb:
          vim.command('bwipeout! ' + str(_b.number))
  del _b
  cb.append('valid: b:%s, cb:%s' % (repr(b.valid), repr(cb.valid)))
  for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc6")', 'b.name = "!"'):
      try:
          exec(expr)
      except vim.error:
          pass
      else:
          # Usually a SEGV here
          # Should not happen in any case
          cb.append('No exception for ' + expr)
  vim.command('cd .')
  del b
EOF
:augroup BUFS
:   autocmd!
:augroup END
:augroup! BUFS
:"
:" Test vim.buffers object
:set hidden
:edit a
:buffer #
:edit b
:buffer #
:edit c
:buffer #
py << trim EOF
  try:
      from __builtin__ import next
  except ImportError:
      next = lambda o: o.next()
  # Check GCing iterator that was not fully exhausted
  i = iter(vim.buffers)
  cb.append('i:' + str(next(i)))
  # and also check creating more than one iterator at a time
  i2 = iter(vim.buffers)
  cb.append('i2:' + str(next(i2)))
  cb.append('i:' + str(next(i)))
  # The following should trigger GC and not cause any problems
  del i
  del i2
  i3 = iter(vim.buffers)
  cb.append('i3:' + str(next(i3)))
  del i3

  prevnum = 0
  for b in vim.buffers:
      # Check buffer order
      if prevnum >= b.number:
          cb.append('!!! Buffer numbers not in strictly ascending order')
      # Check indexing: vim.buffers[number].number == number
      cb.append(str(b.number) + ':' + repr(vim.buffers[b.number]) + '=' + repr(b))
      prevnum = b.number
  del prevnum

  cb.append(str(len(vim.buffers)))

  bnums = list(map(lambda b: b.number, vim.buffers))[1:]

  # Test wiping out buffer with existing iterator
  i4 = iter(vim.buffers)
  cb.append('i4:' + str(next(i4)))
  vim.command('bwipeout! ' + str(bnums.pop(0)))
  try:
      next(i4)
  except vim.error:
      pass
  else:
      cb.append('!!!! No vim.error')
  i4 = iter(vim.buffers)
  vim.command('bwipeout! ' + str(bnums.pop(-1)))
  vim.command('bwipeout! ' + str(bnums.pop(-1)))
  cb.append('i4:' + str(next(i4)))
  try:
      next(i4)
  except StopIteration:
      cb.append('StopIteration')
  del i4
  del bnums
EOF
:"
:" Test vim.{tabpage,window}list and vim.{tabpage,window} objects
:tabnew 0
:tabnew 1
:vnew a.1
:tabnew 2
:vnew a.2
:vnew b.2
:vnew c.2
py << trim EOF
  cb.append('Number of tabs: ' + str(len(vim.tabpages)))
  cb.append('Current tab pages:')
  def W(w):
      if repr(w).find('(unknown)') != -1:
          return '<window object (unknown)>'
      else:
          return repr(w)

  start = len(cb)

  def Cursor(w):
      if w.buffer is cb:
          return repr((start - w.cursor[0], w.cursor[1]))
      else:
          return repr(w.cursor)

  for t in vim.tabpages:
      cb.append('  ' + repr(t) + '(' + str(t.number) + ')' + ': ' + str(len(t.windows)) + ' windows, current is ' + W(t.window))
      cb.append('  Windows:')
      for w in t.windows:
          cb.append('    ' + W(w) + '(' + str(w.number) + ')' + ': displays buffer ' + repr(w.buffer) + '; cursor is at ' + Cursor(w))
          # Other values depend on the size of the terminal, so they are checked partly:
          for attr in ('height', 'row', 'width', 'col'):
              try:
                  aval = getattr(w, attr)
                  if type(aval) is not long:
                      raise TypeError
                  if aval < 0:
                      raise ValueError
              except Exception:
                  cb.append('!!!!!! Error while getting attribute ' + attr + ': ' + sys.exc_type.__name__)
          del aval
          del attr
          w.cursor = (len(w.buffer), 0)
  del W
  del Cursor
  cb.append('Number of windows in current tab page: ' + str(len(vim.windows)))
  if list(vim.windows) != list(vim.current.tabpage.windows):
      cb.append('!!!!!! Windows differ')
EOF
:"
:" Test vim.current
py << trim EOF
  def H(o):
      return repr(o)
  cb.append('Current tab page: ' + repr(vim.current.tabpage))
  cb.append('Current window: ' + repr(vim.current.window) + ': ' + H(vim.current.window) + ' is ' + H(vim.current.tabpage.window))
  cb.append('Current buffer: ' + repr(vim.current.buffer) + ': ' + H(vim.current.buffer) + ' is ' + H(vim.current.window.buffer)+ ' is ' + H(vim.current.tabpage.window.buffer))
  del H
  # Assigning: fails
  try:
      vim.current.window = vim.tabpages[0].window
  except ValueError:
      cb.append('ValueError at assigning foreign tab window')

  for attr in ('window', 'tabpage', 'buffer'):
      try:
          setattr(vim.current, attr, None)
      except TypeError:
          cb.append('Type error at assigning None to vim.current.' + attr)
  del attr

  # Assigning: success
  vim.current.tabpage = vim.tabpages[-2]
  vim.current.buffer = cb
  vim.current.window = vim.windows[0]
  vim.current.window.cursor = (len(vim.current.buffer), 0)
  cb.append('Current tab page: ' + repr(vim.current.tabpage))
  cb.append('Current window: ' + repr(vim.current.window))
  cb.append('Current buffer: ' + repr(vim.current.buffer))
  cb.append('Current line: ' + repr(vim.current.line))
  ws = list(vim.windows)
  ts = list(vim.tabpages)
  for b in vim.buffers:
      if b is not cb:
          vim.command('bwipeout! ' + str(b.number))
  del b
  cb.append('w.valid: ' + repr([w.valid for w in ws]))
  cb.append('t.valid: ' + repr([t.valid for t in ts]))
  del w
  del t
  del ts
  del ws
EOF
:tabonly!
:only!
:"
:" Test types
py << trim EOF
  for expr, attr in (
      ('vim.vars',                         'Dictionary'),
      ('vim.options',                      'Options'),
      ('vim.bindeval("{}")',               'Dictionary'),
      ('vim.bindeval("[]")',               'List'),
      ('vim.bindeval("function(\'tr\')")', 'Function'),
      ('vim.current.buffer',               'Buffer'),
      ('vim.current.range',                'Range'),
      ('vim.current.window',               'Window'),
      ('vim.current.tabpage',              'TabPage'),
  ):
      cb.append(expr + ':' + attr + ':' + repr(type(eval(expr)) is getattr(vim, attr)))
  del expr
  del attr
EOF
:"
:" Test __dir__() method
py << trim EOF
  for name, o in (
          ('current',    vim.current),
          ('buffer',     vim.current.buffer),
          ('window',     vim.current.window),
          ('tabpage',    vim.current.tabpage),
          ('range',      vim.current.range),
          ('dictionary', vim.bindeval('{}')),
          ('list',       vim.bindeval('[]')),
          ('function',   vim.bindeval('function("tr")')),
          ('output',     sys.stdout),
      ):
      cb.append(name + ':' + ','.join(dir(o)))
  del name
  del o
EOF
:"
:" Test vim.*.__new__
:$put =string(pyeval('vim.Dictionary({})'))
:$put =string(pyeval('vim.Dictionary(a=1)'))
:$put =string(pyeval('vim.Dictionary(((''a'', 1),))'))
:$put =string(pyeval('vim.List()'))
:$put =string(pyeval('vim.List(iter(''abc7''))'))
:$put =string(pyeval('vim.Function(''tr'')'))
:$put =string(pyeval('vim.Function(''tr'', args=[123, 3, 4])'))
:$put =string(pyeval('vim.Function(''tr'', args=[])'))
:$put =string(pyeval('vim.Function(''tr'', self={})'))
:$put =string(pyeval('vim.Function(''tr'', args=[123, 3, 4], self={})'))
:$put ='auto_rebind'
:$put =string(pyeval('vim.Function(''tr'', auto_rebind=False)'))
:$put =string(pyeval('vim.Function(''tr'', args=[123, 3, 4], auto_rebind=False)'))
:$put =string(pyeval('vim.Function(''tr'', args=[], auto_rebind=False)'))
:$put =string(pyeval('vim.Function(''tr'', self={}, auto_rebind=False)'))
:$put =string(pyeval('vim.Function(''tr'', args=[123, 3, 4], self={}, auto_rebind=False)'))
:"
:" Test vim.Function
:function Args(...)
:   return a:000
:endfunction
:function SelfArgs(...) dict
:   return [a:000, self]
:endfunction
:" The following four lines should not crash
:let Pt = function('tr', [[]], {'l': []})
:py Pt = vim.bindeval('Pt')
:unlet Pt
:py del Pt
py << trim EOF
  def ecall(out_prefix, func, *args, **kwargs):
      line = out_prefix + ': '
      try:
          ret = func(*args, **kwargs)
      except Exception:
          line += '!exception: ' + emsg(sys.exc_info())
      else:
          line += '!result: ' + vim.Function('string')(ret)
      cb.append(line)
  a = vim.Function('Args')
  pa1 = vim.Function('Args', args=['abcArgsPA1'])
  pa2 = vim.Function('Args', args=[])
  pa3 = vim.Function('Args', args=['abcArgsPA3'], self={'abcSelfPA3': 'abcSelfPA3Val'})
  pa4 = vim.Function('Args', self={'abcSelfPA4': 'abcSelfPA4Val'})
  cb.append('a: ' + repr(a))
  cb.append('pa1: ' + repr(pa1))
  cb.append('pa2: ' + repr(pa2))
  cb.append('pa3: ' + repr(pa3))
  cb.append('pa4: ' + repr(pa4))
  sa = vim.Function('SelfArgs')
  psa1 = vim.Function('SelfArgs', args=['abcArgsPSA1'])
  psa2 = vim.Function('SelfArgs', args=[])
  psa3 = vim.Function('SelfArgs', args=['abcArgsPSA3'], self={'abcSelfPSA3': 'abcSelfPSA3Val'})
  psa4 = vim.Function('SelfArgs', self={'abcSelfPSA4': 'abcSelfPSA4Val'})
  psa5 = vim.Function('SelfArgs', self={'abcSelfPSA5': 'abcSelfPSA5Val'}, auto_rebind=0)
  psa6 = vim.Function('SelfArgs', args=['abcArgsPSA6'], self={'abcSelfPSA6': 'abcSelfPSA6Val'}, auto_rebind=())
  psa7 = vim.Function('SelfArgs', args=['abcArgsPSA7'], auto_rebind=[])
  psa8 = vim.Function('SelfArgs', auto_rebind=False)
  psa9 = vim.Function('SelfArgs', self={'abcSelfPSA9': 'abcSelfPSA9Val'}, auto_rebind=True)
  psaA = vim.Function('SelfArgs', args=['abcArgsPSAA'], self={'abcSelfPSAA': 'abcSelfPSAAVal'}, auto_rebind=1)
  psaB = vim.Function('SelfArgs', args=['abcArgsPSAB'], auto_rebind={'abcARPSAB': 'abcARPSABVal'})
  psaC = vim.Function('SelfArgs', auto_rebind=['abcARPSAC'])
  cb.append('sa: ' + repr(sa))
  cb.append('psa1: ' + repr(psa1))
  cb.append('psa2: ' + repr(psa2))
  cb.append('psa3: ' + repr(psa3))
  cb.append('psa4: ' + repr(psa4))
  cb.append('psa5: ' + repr(psa5))
  cb.append('psa6: ' + repr(psa6))
  cb.append('psa7: ' + repr(psa7))
  cb.append('psa8: ' + repr(psa8))
  cb.append('psa9: ' + repr(psa9))
  cb.append('psaA: ' + repr(psaA))
  cb.append('psaB: ' + repr(psaB))
  cb.append('psaC: ' + repr(psaC))

  psar = vim.Function('SelfArgs', args=[{'abcArgsPSAr': 'abcArgsPSArVal'}], self={'abcSelfPSAr': 'abcSelfPSArVal'})
  psar.args[0]['abcArgsPSAr2'] = [psar.self, psar.args[0]]
  psar.self['rec'] = psar
  psar.self['self'] = psar.self
  psar.self['args'] = psar.args

  try:
      cb.append('psar: ' + repr(psar))
  except Exception:
      cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
EOF
:$put ='s(a): '.string(pyeval('a'))
:$put ='s(pa1): '.string(pyeval('pa1'))
:$put ='s(pa2): '.string(pyeval('pa2'))
:$put ='s(pa3): '.string(pyeval('pa3'))
:$put ='s(pa4): '.string(pyeval('pa4'))
:$put ='s(sa): '.string(pyeval('sa'))
:$put ='s(psa1): '.string(pyeval('psa1'))
:$put ='s(psa2): '.string(pyeval('psa2'))
:$put ='s(psa3): '.string(pyeval('psa3'))
:$put ='s(psa4): '.string(pyeval('psa4'))
:$put ='s(psa5): '.string(pyeval('psa5'))
:$put ='s(psa6): '.string(pyeval('psa6'))
:$put ='s(psa7): '.string(pyeval('psa7'))
:$put ='s(psa8): '.string(pyeval('psa8'))
:$put ='s(psa9): '.string(pyeval('psa9'))
:$put ='s(psaA): '.string(pyeval('psaA'))
:$put ='s(psaB): '.string(pyeval('psaB'))
:$put ='s(psaC): '.string(pyeval('psaC'))
:
:for v in ['sa', 'psa1', 'psa2', 'psa3', 'psa4', 'psa5', 'psa6', 'psa7', 'psa8', 'psa9', 'psaA', 'psaB', 'psaC']
:   let d = {'f': pyeval(v)}
:   $put ='d.'.v.'(): '.string(d.f())
:endfor
:
:py ecall('a()', a, )
:py ecall('pa1()', pa1, )
:py ecall('pa2()', pa2, )
:py ecall('pa3()', pa3, )
:py ecall('pa4()', pa4, )
:py ecall('sa()', sa, )
:py ecall('psa1()', psa1, )
:py ecall('psa2()', psa2, )
:py ecall('psa3()', psa3, )
:py ecall('psa4()', psa4, )
:
:py ecall('a(42, 43)', a, 42, 43)
:py ecall('pa1(42, 43)', pa1, 42, 43)
:py ecall('pa2(42, 43)', pa2, 42, 43)
:py ecall('pa3(42, 43)', pa3, 42, 43)
:py ecall('pa4(42, 43)', pa4, 42, 43)
:py ecall('sa(42, 43)', sa, 42, 43)
:py ecall('psa1(42, 43)', psa1, 42, 43)
:py ecall('psa2(42, 43)', psa2, 42, 43)
:py ecall('psa3(42, 43)', psa3, 42, 43)
:py ecall('psa4(42, 43)', psa4, 42, 43)
:
:py ecall('a(42, self={"20": 1})', a, 42, self={'20': 1})
:py ecall('pa1(42, self={"20": 1})', pa1, 42, self={'20': 1})
:py ecall('pa2(42, self={"20": 1})', pa2, 42, self={'20': 1})
:py ecall('pa3(42, self={"20": 1})', pa3, 42, self={'20': 1})
:py ecall('pa4(42, self={"20": 1})', pa4, 42, self={'20': 1})
:py ecall('sa(42, self={"20": 1})', sa, 42, self={'20': 1})
:py ecall('psa1(42, self={"20": 1})', psa1, 42, self={'20': 1})
:py ecall('psa2(42, self={"20": 1})', psa2, 42, self={'20': 1})
:py ecall('psa3(42, self={"20": 1})', psa3, 42, self={'20': 1})
:py ecall('psa4(42, self={"20": 1})', psa4, 42, self={'20': 1})
:
:py ecall('a(self={"20": 1})', a, self={'20': 1})
:py ecall('pa1(self={"20": 1})', pa1, self={'20': 1})
:py ecall('pa2(self={"20": 1})', pa2, self={'20': 1})
:py ecall('pa3(self={"20": 1})', pa3, self={'20': 1})
:py ecall('pa4(self={"20": 1})', pa4, self={'20': 1})
:py ecall('sa(self={"20": 1})', sa, self={'20': 1})
:py ecall('psa1(self={"20": 1})', psa1, self={'20': 1})
:py ecall('psa2(self={"20": 1})', psa2, self={'20': 1})
:py ecall('psa3(self={"20": 1})', psa3, self={'20': 1})
:py ecall('psa4(self={"20": 1})', psa4, self={'20': 1})
py << trim EOF
  def s(v):
      if v is None:
          return repr(v)
      else:
          return vim.Function('string')(v)

  cb.append('a.args: ' + s(a.args))
  cb.append('pa1.args: ' + s(pa1.args))
  cb.append('pa2.args: ' + s(pa2.args))
  cb.append('pa3.args: ' + s(pa3.args))
  cb.append('pa4.args: ' + s(pa4.args))
  cb.append('sa.args: ' + s(sa.args))
  cb.append('psa1.args: ' + s(psa1.args))
  cb.append('psa2.args: ' + s(psa2.args))
  cb.append('psa3.args: ' + s(psa3.args))
  cb.append('psa4.args: ' + s(psa4.args))

  cb.append('a.self: ' + s(a.self))
  cb.append('pa1.self: ' + s(pa1.self))
  cb.append('pa2.self: ' + s(pa2.self))
  cb.append('pa3.self: ' + s(pa3.self))
  cb.append('pa4.self: ' + s(pa4.self))
  cb.append('sa.self: ' + s(sa.self))
  cb.append('psa1.self: ' + s(psa1.self))
  cb.append('psa2.self: ' + s(psa2.self))
  cb.append('psa3.self: ' + s(psa3.self))
  cb.append('psa4.self: ' + s(psa4.self))

  cb.append('a.name: ' + s(a.name))
  cb.append('pa1.name: ' + s(pa1.name))
  cb.append('pa2.name: ' + s(pa2.name))
  cb.append('pa3.name: ' + s(pa3.name))
  cb.append('pa4.name: ' + s(pa4.name))
  cb.append('sa.name: ' + s(sa.name))
  cb.append('psa1.name: ' + s(psa1.name))
  cb.append('psa2.name: ' + s(psa2.name))
  cb.append('psa3.name: ' + s(psa3.name))
  cb.append('psa4.name: ' + s(psa4.name))

  cb.append('a.auto_rebind: ' + s(a.auto_rebind))
  cb.append('pa1.auto_rebind: ' + s(pa1.auto_rebind))
  cb.append('pa2.auto_rebind: ' + s(pa2.auto_rebind))
  cb.append('pa3.auto_rebind: ' + s(pa3.auto_rebind))
  cb.append('pa4.auto_rebind: ' + s(pa4.auto_rebind))
  cb.append('sa.auto_rebind: ' + s(sa.auto_rebind))
  cb.append('psa1.auto_rebind: ' + s(psa1.auto_rebind))
  cb.append('psa2.auto_rebind: ' + s(psa2.auto_rebind))
  cb.append('psa3.auto_rebind: ' + s(psa3.auto_rebind))
  cb.append('psa4.auto_rebind: ' + s(psa4.auto_rebind))
  cb.append('psa5.auto_rebind: ' + s(psa5.auto_rebind))
  cb.append('psa6.auto_rebind: ' + s(psa6.auto_rebind))
  cb.append('psa7.auto_rebind: ' + s(psa7.auto_rebind))
  cb.append('psa8.auto_rebind: ' + s(psa8.auto_rebind))
  cb.append('psa9.auto_rebind: ' + s(psa9.auto_rebind))
  cb.append('psaA.auto_rebind: ' + s(psaA.auto_rebind))
  cb.append('psaB.auto_rebind: ' + s(psaB.auto_rebind))
  cb.append('psaC.auto_rebind: ' + s(psaC.auto_rebind))

  del s

  del a
  del pa1
  del pa2
  del pa3
  del pa4
  del sa
  del psa1
  del psa2
  del psa3
  del psa4
  del psa5
  del psa6
  del psa7
  del psa8
  del psa9
  del psaA
  del psaB
  del psaC
  del psar

  del ecall
EOF
:"
:" Test stdout/stderr
:redir => messages
:py sys.stdout.write('abc8') ; sys.stdout.write('def')
:py sys.stderr.write('abc9') ; sys.stderr.write('def')
:py sys.stdout.writelines(iter('abcA'))
:py sys.stderr.writelines(iter('abcB'))
:redir END
:$put =string(substitute(messages, '\d\+', '', 'g'))
:" Test subclassing
:fun Put(...)
:   $put =string(a:000)
:   return a:000
:endfun
py << trim EOF
  class DupDict(vim.Dictionary):
      def __setitem__(self, key, value):
          super(DupDict, self).__setitem__(key, value)
          super(DupDict, self).__setitem__('dup_' + key, value)
  dd = DupDict()
  dd['a'] = 'b'

  class DupList(vim.List):
      def __getitem__(self, idx):
          return [super(DupList, self).__getitem__(idx)] * 2

  dl = DupList()
  dl2 = DupList(iter('abcC'))
  dl.extend(dl2[0])

  class DupFun(vim.Function):
      def __call__(self, arg):
          return super(DupFun, self).__call__(arg, arg)

  df = DupFun('Put')
EOF
:$put =string(sort(keys(pyeval('dd'))))
:$put =string(pyeval('dl'))
:$put =string(pyeval('dl2'))
:$put =string(pyeval('df(2)'))
:$put =string(pyeval('dl') is# pyeval('dl'))
:$put =string(pyeval('dd') is# pyeval('dd'))
:$put =string(pyeval('df'))
:delfunction Put
py << trim
  del DupDict
  del DupList
  del DupFun
  del dd
  del dl
  del dl2
  del df
.
:"
:" Test chdir
py << trim EOF
  import os
  fnamemodify = vim.Function('fnamemodify')
  cb.append(fnamemodify('.', ':p:h:t'))
  cb.append(vim.eval('@%'))
  os.chdir('..')
  path = fnamemodify('.', ':p:h:t')
  if path != 'src':
    # Running tests from a shadow directory, so move up another level
    # This will result in @% looking like shadow/testdir/test86.in, hence the
    # extra fnamemodify
    os.chdir('..')
    cb.append(fnamemodify('.', ':p:h:t'))
    cb.append(fnamemodify(vim.eval('@%'), ':s?^%s.??' % path).replace(os.path.sep, '/'))
    os.chdir(path)
    del path
  else:
    cb.append(fnamemodify('.', ':p:h:t'))
    cb.append(vim.eval('@%').replace(os.path.sep, '/'))
  os.chdir('testdir')
  cb.append(fnamemodify('.', ':p:h:t'))
  cb.append(vim.eval('@%'))
  del fnamemodify
EOF
:"
:" Test errors
:fun F() dict
:endfun
:fun D()
:endfun
py << trim EOF
  d = vim.Dictionary()
  ned = vim.Dictionary(foo='bar', baz='abcD')
  dl = vim.Dictionary(a=1)
  dl.locked = True
  l = vim.List()
  ll = vim.List('abcE')
  ll.locked = True
  nel = vim.List('abcO')
  f = vim.Function('string')
  fd = vim.Function('F')
  fdel = vim.Function('D')
  vim.command('delfunction D')

  def subexpr_test(expr, name, subexprs):
      cb.append('>>> Testing %s using %s' % (name, expr))
      for subexpr in subexprs:
          ee(expr % subexpr)
      cb.append('<<< Finished')

  def stringtochars_test(expr):
      return subexpr_test(expr, 'StringToChars', (
          '1',       # Fail type checks
          'u"\\0"',  # Fail PyString_AsStringAndSize(bytes, , NULL) check
          '"\\0"',   # Fail PyString_AsStringAndSize(object, , NULL) check
      ))

  class Mapping(object):
      def __init__(self, d):
          self.d = d

      def __getitem__(self, key):
          return self.d[key]

      def keys(self):
          return self.d.keys()

      def items(self):
          return self.d.items()

  def convertfrompyobject_test(expr, recurse=True):
      # pydict_to_tv
      stringtochars_test(expr % '{%s : 1}')
      if recurse:
          convertfrompyobject_test(expr % '{"abcF" : %s}', False)
      # pymap_to_tv
      stringtochars_test(expr % 'Mapping({%s : 1})')
      if recurse:
          convertfrompyobject_test(expr % 'Mapping({"abcG" : %s})', False)
      # pyseq_to_tv
      iter_test(expr)
      return subexpr_test(expr, 'ConvertFromPyObject', (
          'None',                 # Not conversible
          '{"": 1}',              # Empty key not allowed
          '{u"": 1}',             # Same, but with unicode object
          'FailingMapping()',     #
          'FailingMappingKey()',  #
          'FailingNumber()',      #
      ))

  def convertfrompymapping_test(expr):
      convertfrompyobject_test(expr)
      return subexpr_test(expr, 'ConvertFromPyMapping', (
          '[]',
      ))

  def iter_test(expr):
      return subexpr_test(expr, '*Iter*', (
          'FailingIter()',
          'FailingIterNext()',
      ))

  def number_test(expr, natural=False, unsigned=False):
      if natural:
          unsigned = True
      return subexpr_test(expr, 'NumberToLong', (
          '[]',
          'None',
      ) + (unsigned and ('-1',) or ())
      + (natural and ('0',) or ()))

  class FailingTrue(object):
      def __nonzero__(self):
          raise NotImplementedError('bool')

  class FailingIter(object):
      def __iter__(self):
          raise NotImplementedError('iter')

  class FailingIterNext(object):
      def __iter__(self):
          return self

      def next(self):
          raise NotImplementedError('next')

  class FailingIterNextN(object):
      def __init__(self, n):
          self.n = n

      def __iter__(self):
          return self

      def next(self):
          if self.n:
              self.n -= 1
              return 1
          else:
              raise NotImplementedError('next N')

  class FailingMappingKey(object):
      def __getitem__(self, item):
          raise NotImplementedError('getitem:mappingkey')

      def keys(self):
          return list("abcH")

  class FailingMapping(object):
      def __getitem__(self):
          raise NotImplementedError('getitem:mapping')

      def keys(self):
          raise NotImplementedError('keys')

  class FailingList(list):
      def __getitem__(self, idx):
          if i == 2:
              raise NotImplementedError('getitem:list')
          else:
              return super(FailingList, self).__getitem__(idx)

  class NoArgsCall(object):
      def __call__(self):
          pass

  class FailingCall(object):
      def __call__(self, path):
          raise NotImplementedError('call')

  class FailingNumber(object):
      def __int__(self):
          raise NotImplementedError('int')

  cb.append("> Output")
  cb.append(">> OutputSetattr")
  ee('del sys.stdout.softspace')
  number_test('sys.stdout.softspace = %s', unsigned=True)
  number_test('sys.stderr.softspace = %s', unsigned=True)
  ee('assert sys.stdout.isatty()==False')
  ee('assert sys.stdout.seekable()==False')
  ee('sys.stdout.close()')
  ee('sys.stdout.flush()')
  ee('assert sys.stderr.isatty()==False')
  ee('assert sys.stderr.seekable()==False')
  ee('sys.stderr.close()')
  ee('sys.stderr.flush()')
  ee('sys.stdout.attr = None')
  cb.append(">> OutputWrite")
  ee('assert sys.stdout.writable()==True')
  ee('assert sys.stdout.readable()==False')
  ee('assert sys.stderr.writable()==True')
  ee('assert sys.stderr.readable()==False')
  ee('assert sys.stdout.closed()==False')
  ee('assert sys.stderr.closed()==False')
  ee('assert sys.stdout.errors=="strict"')
  ee('assert sys.stderr.errors=="strict"')
  ee('assert sys.stdout.encoding==sys.stderr.encoding')
  ee('sys.stdout.write(None)')
  cb.append(">> OutputWriteLines")
  ee('sys.stdout.writelines(None)')
  ee('sys.stdout.writelines([1])')
  iter_test('sys.stdout.writelines(%s)')
  cb.append("> VimCommand")
  stringtochars_test('vim.command(%s)')
  ee('vim.command("", 2)')
  #! Not checked: vim->python exceptions translating: checked later
  cb.append("> VimToPython")
  #! Not checked: everything: needs errors in internal python functions
  cb.append("> VimEval")
  stringtochars_test('vim.eval(%s)')
  ee('vim.eval("", FailingTrue())')
  #! Not checked: everything: needs errors in internal python functions
  cb.append("> VimEvalPy")
  stringtochars_test('vim.bindeval(%s)')
  ee('vim.eval("", 2)')
  #! Not checked: vim->python exceptions translating: checked later
  cb.append("> VimStrwidth")
  stringtochars_test('vim.strwidth(%s)')
  cb.append("> VimForeachRTP")
  ee('vim.foreach_rtp(None)')
  ee('vim.foreach_rtp(NoArgsCall())')
  ee('vim.foreach_rtp(FailingCall())')
  ee('vim.foreach_rtp(int, 2)')
  cb.append('> import')
  old_rtp = vim.options['rtp']
  vim.options['rtp'] = os.getcwd().replace('\\', '\\\\').replace(',', '\\,')
  ee('import xxx_no_such_module_xxx')
  ee('import failing_import')
  ee('import failing')
  vim.options['rtp'] = old_rtp
  del old_rtp
  cb.append("> Options")
  cb.append(">> OptionsItem")
  ee('vim.options["abcQ"]')
  ee('vim.options[""]')
  stringtochars_test('vim.options[%s]')
  cb.append(">> OptionsContains")
  stringtochars_test('%s in vim.options')
  cb.append("> Dictionary")
  cb.append(">> DictionaryConstructor")
  ee('vim.Dictionary("abcI")')
  ##! Not checked: py_dict_alloc failure
  cb.append(">> DictionarySetattr")
  ee('del d.locked')
  ee('d.locked = FailingTrue()')
  ee('vim.vvars.locked = False')
  ee('d.scope = True')
  ee('d.xxx = True')
  cb.append(">> _DictionaryItem")
  ee('d.get("a", 2, 3)')
  stringtochars_test('d.get(%s)')
  ee('d.pop("a")')
  ee('dl.pop("a")')
  cb.append(">> DictionaryContains")
  ee('"" in d')
  ee('0 in d')
  cb.append(">> DictionaryIterNext")
  ee('for i in ned: ned["a"] = 1')
  del i
  cb.append(">> DictionaryAssItem")
  ee('dl["b"] = 1')
  stringtochars_test('d[%s] = 1')
  convertfrompyobject_test('d["a"] = %s')
  cb.append(">> DictionaryUpdate")
  cb.append(">>> kwargs")
  cb.append(">>> iter")
  ee('d.update(FailingMapping())')
  ee('d.update([FailingIterNext()])')
  ee('d.update([FailingIterNextN(1)])')
  iter_test('d.update(%s)')
  convertfrompyobject_test('d.update(%s)')
  stringtochars_test('d.update(((%s, 0),))')
  convertfrompyobject_test('d.update((("a", %s),))')
  cb.append(">> DictionaryPopItem")
  ee('d.popitem(1, 2)')
  cb.append(">> DictionaryHasKey")
  ee('d.has_key()')
  cb.append("> List")
  cb.append(">> ListConstructor")
  ee('vim.List(1, 2)')
  ee('vim.List(a=1)')
  iter_test('vim.List(%s)')
  convertfrompyobject_test('vim.List([%s])')
  cb.append(">> ListItem")
  ee('l[1000]')
  cb.append(">> ListAssItem")
  ee('ll[1] = 2')
  ee('l[1000] = 3')
  cb.append(">> ListAssSlice")
  ee('ll[1:100] = "abcJ"')
  iter_test('l[:] = %s')
  ee('nel[1:10:2]  = "abcK"')
  cb.append(repr(tuple(nel)))
  ee('nel[1:10:2]  = "a"')
  cb.append(repr(tuple(nel)))
  ee('nel[1:1:-1]  = "a"')
  cb.append(repr(tuple(nel)))
  ee('nel[:] = FailingIterNextN(2)')
  cb.append(repr(tuple(nel)))
  convertfrompyobject_test('l[:] = [%s]')
  cb.append(">> ListConcatInPlace")
  iter_test('l.extend(%s)')
  convertfrompyobject_test('l.extend([%s])')
  cb.append(">> ListSetattr")
  ee('del l.locked')
  ee('l.locked = FailingTrue()')
  ee('l.xxx = True')
  cb.append("> Function")
  cb.append(">> FunctionConstructor")
  cb.append(">>> FunctionConstructor")
  ee('vim.Function("123")')
  ee('vim.Function("xxx_non_existent_function_xxx")')
  ee('vim.Function("xxx#non#existent#function#xxx")')
  ee('vim.Function("xxx_non_existent_function_xxx2", args=[])')
  ee('vim.Function("xxx_non_existent_function_xxx3", self={})')
  ee('vim.Function("xxx_non_existent_function_xxx4", args=[], self={})')
  cb.append(">>> FunctionNew")
  ee('vim.Function("tr", self="abcFuncSelf")')
  ee('vim.Function("tr", args=427423)')
  ee('vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2")')
  ee('vim.Function(self="abcFuncSelf2", args="abcFuncArgs2")')
  ee('vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2")')
  ee('vim.Function("tr", "")')
  cb.append(">> FunctionCall")
  convertfrompyobject_test('f(%s)')
  convertfrompymapping_test('fd(self=%s)')
  cb.append("> TabPage")
  cb.append(">> TabPageAttr")
  ee('vim.current.tabpage.xxx')
  cb.append("> TabList")
  cb.append(">> TabListItem")
  ee('vim.tabpages[1000]')
  cb.append("> Window")
  cb.append(">> WindowAttr")
  ee('vim.current.window.xxx')
  cb.append(">> WindowSetattr")
  ee('vim.current.window.buffer = 0')
  ee('vim.current.window.cursor = (100000000, 100000000)')
  ee('vim.current.window.cursor = True')
  number_test('vim.current.window.height = %s', unsigned=True)
  number_test('vim.current.window.width = %s', unsigned=True)
  ee('vim.current.window.xxxxxx = True')
  cb.append("> WinList")
  cb.append(">> WinListItem")
  ee('vim.windows[1000]')
  cb.append("> Buffer")
  cb.append(">> StringToLine (indirect)")
  ee('vim.current.buffer[0] = u"\\na"')
  ee('vim.current.buffer[0] = "\\na"')
  cb.append(">> SetBufferLine (indirect)")
  ee('vim.current.buffer[0] = True')
  cb.append(">> SetBufferLineList (indirect)")
  ee('vim.current.buffer[:] = True')
  ee('vim.current.buffer[:] = ["\\na", "bc"]')
  cb.append(">> InsertBufferLines (indirect)")
  ee('vim.current.buffer.append(None)')
  ee('vim.current.buffer.append(["\\na", "bc"])')
  ee('vim.current.buffer.append("\\nbc")')
  cb.append(">> RBItem")
  ee('vim.current.buffer[100000000]')
  cb.append(">> RBAsItem")
  ee('vim.current.buffer[100000000] = ""')
  cb.append(">> BufferAttr")
  ee('vim.current.buffer.xxx')
  cb.append(">> BufferSetattr")
  ee('vim.current.buffer.name = True')
  ee('vim.current.buffer.xxx = True')
  cb.append(">> BufferMark")
  ee('vim.current.buffer.mark(0)')
  ee('vim.current.buffer.mark("abcM")')
  ee('vim.current.buffer.mark("!")')
  cb.append(">> BufferRange")
  ee('vim.current.buffer.range(1, 2, 3)')
  cb.append("> BufMap")
  cb.append(">> BufMapItem")
  ee('vim.buffers[100000000]')
  number_test('vim.buffers[%s]', natural=True)
  cb.append("> Current")
  cb.append(">> CurrentGetattr")
  ee('vim.current.xxx')
  cb.append(">> CurrentSetattr")
  ee('vim.current.line = True')
  ee('vim.current.buffer = True')
  ee('vim.current.window = True')
  ee('vim.current.tabpage = True')
  ee('vim.current.xxx = True')
  del d
  del ned
  del dl
  del l
  del ll
  del nel
  del f
  del fd
  del fdel
  del subexpr_test
  del stringtochars_test
  del Mapping
  del convertfrompyobject_test
  del convertfrompymapping_test
  del iter_test
  del number_test
  del FailingTrue
  del FailingIter
  del FailingIterNext
  del FailingIterNextN
  del FailingMapping
  del FailingMappingKey
  del FailingList
  del NoArgsCall
  del FailingCall
  del FailingNumber
EOF
:delfunction F
:"
:" Test import
py << trim EOF
  sys.path.insert(0, os.path.join(os.getcwd(), 'python_before'))
  sys.path.append(os.path.join(os.getcwd(), 'python_after'))
  vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\')
  l = []
  def callback(path):
      l.append(path[-len('/testdir'):].replace(os.path.sep, '/'))
  vim.foreach_rtp(callback)
  cb.append(repr(l))
  del l
  def callback(path):
      return path[-len('/testdir'):].replace(os.path.sep, '/')
  cb.append(repr(vim.foreach_rtp(callback)))
  del callback
  from module import dir as d
  from modulex import ddir
  cb.append(d + ',' + ddir)
  import before
  cb.append(before.dir)
  import after
  cb.append(after.dir)
  import topmodule as tm
  import topmodule.submodule as tms
  import topmodule.submodule.subsubmodule.subsubsubmodule as tmsss
  cb.append(tm.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/__init__.py'):])
  cb.append(tms.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/__init__.py'):])
  cb.append(tmsss.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/subsubmodule/subsubsubmodule.py'):])
  del before
  del after
  del d
  del ddir
  del tm
  del tms
  del tmsss
EOF
:"
:" Test exceptions
:fun Exe(e)
:   execute a:e
:endfun
py << trim EOF
  Exe = vim.bindeval('function("Exe")')
  ee('vim.command("throw \'abcN\'")')
  ee('Exe("throw \'def\'")')
  ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")')
  ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")')
  ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")')
  ee('vim.eval("xxx_unknown_function_xxx()")')
  ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")')
  del Exe
EOF
:delfunction Exe
:"
:" Regression: interrupting vim.command propagates to next vim.command
py << trim EOF
  def test_keyboard_interrupt():
      try:
          vim.command('while 1 | endwhile')
      except KeyboardInterrupt:
          cb.append('Caught KeyboardInterrupt')
      except Exception:
          cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
      else:
          cb.append('!!!!!!!! No exception')
      try:
          vim.command('$ put =\'Running :put\'')
      except KeyboardInterrupt:
          cb.append('!!!!!!!! Caught KeyboardInterrupt')
      except Exception:
          cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
      else:
          cb.append('No exception')
EOF
:debuggreedy
:call inputsave()
:call feedkeys("s\ns\ns\ns\nq\n")
:redir => output
:debug silent! py test_keyboard_interrupt()
:redir END
:0 debuggreedy
:call inputrestore()
:silent $put =output
:unlet output
:py del test_keyboard_interrupt
:"
:" Cleanup
py << trim EOF
  del cb
  del ee
  del emsg
  del sys
  del os
  del vim
EOF
:endfun
:"
:fun RunTest()
:let checkrefs = !empty($PYTHONDUMPREFS)
:let start = getline(1, '$')
:for i in range(checkrefs ? 10 : 1)
:   if i != 0
:       %d _
:       call setline(1, start)
:   endif
:   call Test()
:   if i == 0
:       let result = getline(1, '$')
:   endif
:endfor
:if checkrefs
:   %d _
:   call setline(1, result)
:endif
:endfun
:"
:call RunTest()
:delfunction RunTest
:delfunction Test
:call garbagecollect(1)
:"
:/^start:/,$wq! test.out
:" vim: et ts=4 isk-=\:
:while getchar(0) isnot 0|endwhile
ENDTEST

start:
